/* 公共-工具类 */
import {
    Loading
} from 'element-ui';
import {
    Message
} from 'element-ui';
import {
    MessageBox
} from 'element-ui';
import FV from '@/utils/FV';
import C from '@/utils/C';
import moment from 'moment'
import _ from 'lodash'
import QRCode from 'qrcodejs2'


export default {
    /*************基本工具--begin******************/

    /**
     * 获取-浏览器可视高度
     * @param h 减去的高度
     */
    fullHeight(h = 0) {
        return document.documentElement.clientHeight - h;
    },

    /**
     * 获取-浏览器可视宽度
     * @param w 减去的宽度
     */
    fullWidth(w = 0) {
        return document.documentElement.clientWidth - w;
    },

    /**
     * 获取-table列表的body高度（去除布局头部、底部）
     * @param h 减去的高度
     */
    dataGridHeight(h = 0) {
        return document.documentElement.clientHeight - 130 - h;
    },

    /**
     * 设置发送短信按钮数据
     * @param {*} sb 发送短信按钮对象
     */
    setSmsBtn(sb) {
        let timer = setInterval(() => {
            if (sb.time > 0) {
                sb.load = true;
                sb.time = sb.time - 1;
                sb.text = `${sb.time}秒后重发`;
                sb.timer = timer;
            } else {
                if (sb.timer) clearInterval(sb.timer);

                sb.load = false;
                sb.time = sb.maxtime;
                sb.text = '重新发送';
                sb.timer = null;
            }
        }, 1000);
    },

    /**
     * 验证字符串是否为空
     * @param {String} str 验证的字符串
     */
    isEmpty(str) {
        if (str == null || str == '' || str == undefined) return true;
        return false;
    },

    /**
     * 去掉字符串前后空格
     * @param v 操作的值
     * @returns 新的字符串
     */
    trim(v) {
        v = v + '';
        return v.replace(/(^\s*)|(\s*$)/g, '');
    },

    /**
     * 去掉字符串所有空格
     * @param v 操作的值
     * @returns 新的字符串
     */
    trimAll(v) {
        v = v + '';
        return v.replace(/\s*/g, '');
    },

    /*************基本工具--end********************/

    /**
     * 对象数组根据某个字段排序
     * @param {Array} arr 
     * @param {string} key 
     */
    sortByKey(arr, key) {
        return arr.sort((a, b) => {
            let x = a[key],
                y = b[key];
            return ((x < y) ? -1 : ((x > y) ? 1 : 0));
        });
    },


    /*************全局提示框--begin******************/
    /**
     * 全局加载
     * @param text 加载显示的文本
     */
    qloading(text) {
        let loading = Loading.service({
            lock: true,
            text: text || '加载中...',
            spinner: 'el-icon-loading',
            background: 'rgba(0, 0, 0, 0.5)',
            customClass: 'my-loading'
        });
        C.fullLoading = loading;
        return C.fullLoading;
    },

    /**
     * 关闭-全局加载
     * @param loading 加载loading实例
     */
    qclose(loading) {
        setTimeout(() => {
            loading.close();
        }, 300);
    },

    /**
     * 全局提示-信息框
     * @param text 提示文本
     */
    qinfo(text) {
        this.qmsg(text, 'info', 3000, true);
    },

    /**
     * 全局提示-成功信息框
     * @param text 提示文本
     */
    qsuccess(text) {
        this.qmsg(text, 'success', 3000, true);
    },

    /**
     * 全局提示-警告信息框
     * @param text 提示文本
     */
    qwarning(text) {
        this.qmsg(text, 'warning', 3000, true);
    },

    /**
     * 全局提示-错误信息框
     * @param text 提示文本
     */
    qerror(text, time = 3000) {
        this.qmsg(text, 'error', time, true);
        return false;
    },

    /**
     * 全局提示-错误信息框
     * @param text 提示文本
     */
    qerrorFalse(text) {
        this.qmsg(text, 'error', 3000, true);
        return false;
    },

    /**
     * 全局提示-信息框
     * @param text      提示文本
     * @param type      提示框类型
     * @param duration  延时关闭时间
     * @param showClose 是否显示关闭按钮
     * @param dangerouslyUseHTMLString 是否将文本内容被当作HTML片段处理
     */
    qmsg(text, type, duration, showClose) {
        let isExitHtml = (text && text.indexOf('</') != -1) ? true : false;
        return Message({
            showClose: showClose || true,
            message: text || '这是一条信息框',
            dangerouslyUseHTMLString: isExitHtml,
            type: type || 'success',
            duration: duration || 3000
        });
    },

    /*************全局提示框--end********************/


    /*************全局弹框--begin********************/

    /**
     * 显示图片
     * @param {*} imgUrl    图片url或者图片字符串
     */
    showImg(imgUrl) {
        const that = this;

        // 可视区域-宽度
        const screenWidth = document.body.clientWidth;
        // 可视区域-高度
        const screenHeight = document.body.clientHeight;

        let img = new Image();
        img.src = imgUrl;
        img.onload = function () {
            if (img.width > screenWidth) {
                img.width = screenWidth - 50;
            }

            if (img.height > screenHeight) {
                img.height = screenHeight - 50;
            }

            let ht = '<img src="' + img.src + '" width="' + img.width + '" height="' + img.height + '" />';

            MessageBox.alert(ht, '查看大图', {
                dangerouslyUseHTMLString: true
            });
        }
    },

    /**
     * 确认弹框
     * @param {*} text          文本
     * @param {*} title         标题
     * @param {*} okbtn         确认按钮文本
     * @param {*} calancebtn    取消按钮文本
     * @param {*} type          弹框类型
     */
    qconfim(text = '确定此操作吗？', title = '提示', okbtn = '确定', calancebtn = '取消', type = 'warning') {
        let isExitHtml = text.indexOf('</') != -1 ? true : false;

        return MessageBox.confirm(text, title, {
            dangerouslyUseHTMLString: isExitHtml,
            confirmButtonText: okbtn,
            cancelButtonText: calancebtn,
            type: type
        }).catch(err => {});
    },
    /**
     * 提交内容弹框
     * @param {*} text          文本
     * @param {*} title         标题
     * @param {*} okbtn         确认按钮文本
     * @param {*} calancebtn    取消按钮文本
     * @param {*} reg           输入框的验证正则表达式
     * @param {*} errorMsg      输入框的验证不通过提示信息
     */
    qprompt(text = '请输入内容', title = '提示', okbtn = '确定', calancebtn = '取消', reg = null, errorMsg = '') {
        let isExitHtml = text.indexOf('</') != -1 ? true : false;

        return MessageBox.prompt(text, title, {
            dangerouslyUseHTMLString: isExitHtml,
            confirmButtonText: okbtn,
            cancelButtonText: calancebtn,
            inputPattern: reg,
            inputErrorMessage: errorMsg,
            closeOnClickModal: false,
        });
    },

    /**
     * 确认弹框
     * @param {*} text          文本
     * @param {*} title         标题
     * @param {*} okbtn         确认按钮
     * @param {*} callback      确认按钮回调方法
     */
    qalert(text = '信息弹框', title = '提示', okbtn = '确定', callback) {
        let isExitHtml = text.indexOf('</') != -1 ? true : false;

        return MessageBox.alert(text, title, {
            dangerouslyUseHTMLString: isExitHtml,
            confirmButtonText: okbtn,
            callback: action => {
                if (callback) callback();
            }
        }).catch(err => {});
    },


    /*************全局弹框--end********************/


    /*************数字处理工具--begin********************/

    /**
     * 数字处理
     * @param {*} val 处理的数字 
     * @param {*} len 保留小数位数
     */
    num(val, len) {
        if (!val) return 0;

        // 默认保留两位小数
        if (!len) len = 2;

        val = val + '';

        if (!FV.isNumber(val)) {
            return val; // 不是数字，原字符返回
        } else if (val.indexOf('.') != -1) {
            // 存在小数点，判断小数点后面的位数，如果大于传入的长度，则截取
            let v = val.substr(val.indexOf('.') + 1);
            if (v.length > len) val = val.substr(0, val.indexOf('.') + len + 1);

            return parseFloat(val);
        } else {
            // 不存在小数点，转换成浮点数字返回
            return parseFloat(val);
        }
    },

    /**
     * 清除-表单某个字段的验证信息
     * @param that      当前组件
     * @param fmname    表单名称
     * @param filed     字段名
     */
    clearFmVali(that, fname, filed) {
        let fields = that.$refs[fname].fields;

        fields.map(it => {
            // 通过prop属性值相同来判断是哪个输入框，比如：要移除prop为'user'
            if (it.prop === filed) {
                // 清除验证信息
                it.clearValidate();

                return false;
            }
        });
    },

    /*************时间处理工具--begin********************/

    /**
     * 获取-两个时间之间相差多少天
     * @param stime 开始时间
     * @param etime 结束时间
     */
    getSEDifferDay(stime, etime) {
        let that = this,
            day = 0;

        let st = that.str2date(stime),
            et = that.str2date(etime);

        day = (et.getTime() - st.getTime()) / (1000 * 60 * 60 * 24);

        return (day).toFixed(0);
    },

    /**
     * 获取-今天起止时间段 如：[2021-05-01 00:00:00, 2021-05-01 23:59:59]
     * @param day 增加的天数（可为负数）
     */
    getSEToday(day) {
        const that = this;

        let todaytime = new Date().getTime();
        if (day && FV.isPosOrNegInt(day)) {
            day = parseInt(day);
            if (day != 0) todaytime += day * 1000 * 60 * 60 * 24;
        }

        let date = that.date2str(new Date(todaytime), 'yyyy-MM-dd');
        return [`${date} 00:00:00`, `${date} 23:59:59`];
    },

    /**
     * 少于2位补0
     * @param n 传入的数字
     */
    pad(n) {
        return (n < 10 ? `0${n}` : n.toString());
    },

    /**
     * 将一个时间字符串转换成时间类型
     * @param dateStr 时间字符串
     * @returns 时间对象
     */
    str2date(dateStr) {
        if (dateStr.indexOf('-') != -1) dateStr = dateStr.replace(/-/g, "/");
        return new Date(dateStr);
    },

    /**
     * 将一个时间类型转换成字符串
     * @param date 时间对象
     * @returns 时间字符串
     */
    date2str(date, fmt) {
        return this.datefmt(date, fmt);
    },

    /**
     * 时间转换为字符串
     * @param date 时间
     * @param format 格式
     */
    datefmt(date, format = 'yyyy-MM-dd HH:mm') {
        let fmt = format;

        // 传入的是时间字符串，则转换成时间类型
        if (typeof (date) === 'string') date = this.str2date(date);

        if (format.indexOf('-') != -1) format = format.replace(/-/g, '/');

        const pad = (n) => (n < 10 ? `0${n}` : n.toString());

        let res = format
            .replace('yyyy', date.getFullYear())
            .replace('MM', pad(date.getMonth() + 1))
            .replace('dd', pad(date.getDate()))
            .replace('HH', pad(date.getHours()))
            .replace('mm', pad(date.getMinutes()))
            .replace('ss', pad(date.getSeconds()));

        if (fmt.indexOf('-') != -1) res = res.replace(/\//g, '-');

        return res;
    },
    /**
     * 为指定时间添加指定天数
     * @param {*} date 时间（可为时间对象） eg: 2021-10-10 或者 2021-10-10 10:10:10
     * @param {*} day 天数（可为负数）
     * @param {*} fmt 返回的格式 默认：yyyy-MM-dd
     * @returns 指定格式的字符串
     */
    addDayForTime(date, day, fmt = 'yyyy-MM-dd') {
        const that = this;

        // 将天数转换成数字
        day = Number(day);

        if (date instanceof Date) {
            // 传入的date是时间对象
            return that.date2str(new Date(date.getTime() + 1000 * 60 * 60 * 24 * day), fmt);
        } else {
            // 传入的date是时间字符串
            return that.date2str(new Date(that.str2date(date).getTime() + 1000 * 60 * 60 * 24 * day), fmt);
        }
    },

    /*************时间处理工具--end********************/

    /************字符串处理工具--begin*****************/
    /**
     * 深度拷贝对象
     * @param obj 要拷贝的对象
     * @returns obj-返回一个新的对象
     */
    scopy(obj) {
        // return JSON.parse(JSON.stringify(obj));
        return _.cloneDeep(obj);
    },
    /**
     * 浅拷贝对象（切断对象之间的联系）
     * @param obj 要拷贝的对象
     * @returns obj-返回一个新的对象
     */
    qcopy(obj) {
        return Object.assign(obj);
    },
    /**
     * 给表单参数对象设置值，数组参数
     * @param {*} ps 设置的参数对象
     * @param {*} val 设置的值
     *  // if(e.val.length > 0){
     *  //   e.val.forEach(it => { that.fm.ps.deptId.push(it); });
     *  // }else{
     *  //   that.fm.ps.deptId.length = 0;
     *  // }
     */
    setFmArr(ps = [], val) {
        if (val.length == 0) {
            ps.length = 0;
        } else {
            ps.length = 0; // 清空一下
            val.forEach(it => {
                ps.push(it);
            });
        }
        return ps;
    },

    /**
     * 提取身份证信息
     * @param {String} idCard 身份证号码
     * @param {*} separator 分隔字符 eg：‘-’，‘/’（默认）
     * @returns {age=年龄, birthday=生日年月日, gender=性别}
     * if (!/(^\d{8}(0\d|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/.test(idCard)) {
     */
    getIdCardInfo(idCard, separator = '/') {
        if (!/(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/.test(idCard)) {
            this.qinfo(`${idCard}不是一个身份证号码`);
            return {
                age: '',
                birthday: '',
                gender: ''
            };
        }

        // 提取 idCard 中的字符
        const idSubstr = (s, e) => idCard.substr(s, e);

        // 拼接日期
        const splice = d => d.join(separator);

        // 获取出生年月日 性别（0 女 1 男）
        let birthday, gender;
        if (idCard.length === 18) {
            birthday = splice([idSubstr(6, 4), idSubstr(10, 2), idSubstr(12, 2)]);
            gender = idSubstr(-2, 1) & 1;
        } else {
            birthday = splice(idSubstr(6, 2), idSubstr(8, 2), idSubstr(10, 2));
            gender = idSubstr(-1, 1) & 1;
        }

        // 获取年龄（实岁）
        const birthDate = new Date(birthday);
        const newDate = new Date();
        const year = newDate.getFullYear();
        let age = year - birthDate.getFullYear();
        if (newDate < new Date(splice([year, birthday.substring(5)]))) {
            age--;
        };

        return {
            age,
            birthday,
            gender
        };
    },

    /************字符串处理工具--end*******************/

    /**
     * 线型数据转树形数据
     * @param list 欲处理的数据列表
     * @param pname 父级标志名称 如：pid
     */
    line2treeData(list, pname) {
        const that = this;

        if (!list || list.length == 0) return list;

        let map = {};
        // 遍历第一遍，以id为键，保存起来，方便获取
        list.forEach(it => {
            map[it.id] = it;
        });
        // console.log('1.处理后的数据', map);

        // 遍历第二遍，为父级添加子集   
        for (let it in map) {
            let c = map[it],
                p = map[c[pname]];

            if (p) {
                if (p.children) {
                    p.children.push(c);
                } else {
                    p.children = [c];
                }
            } else { // 不存在，则保存为顶级（去掉这里，不然elementui的cascader组件取不了没有子集的选项值）
                // c.children = [];
            }
        }

        // console.log('2.处理后的数据', map);

        // 第三遍遍历，排除不是父级的数据
        let reslist = [];
        for (let it in map) {
            let c = map[it];
            // 父级不存在的对象保存
            if (!map[c[pname]]) reslist.push(map[it]);
        }

        // console.log('3.处理后的数据', reslist);
        return reslist;
    },

    /**
     * 查找树形结构中的某个指定对象（需存在children属性）
     * @param {*} treeList 树形列表 
     * @param {*} findName 查找的属性名字符串
     * @param {*} findVal  查找的属性值
     * @returns 结果对象
     */
    findTreeListObj(treeList, findName, findVal) {
        const queue = [...treeList]
        while (queue.length) {
            const o = queue.shift()
            if (o[findName] === findVal) return o;
            queue.push(...(o.children || []));
        }
    },
    /**
     * 
     * @param {*} idName    查找的标志属性名称（标志的值才能作为查找依据）
     * @param {*} pidName   查找的标志父级属性名称（一般是pid）
     * @param {*} sid       标志属性开始值
     * @param {*} eid       标志属性结束值（默认0或者空，因为结束即找到了顶级）
     */
    findP2CArr(list, idName, pidName, sid, eid) {
        let res = []; // 结果数组

        if (!list || list.length == 0) return res;

        let map = {};
        // 遍历第一遍，以id为键，保存起来，方便获取
        list.forEach(it => {
            map[it[idName]] = it;
        });
        // 获取-开始id的对象
        let sobj = map[sid];
        // console.log(sobj, sid, 'sobj')
        if (!sobj) {
            return res; // 没有开始id对象，也返回
        } else {
            res.push(sobj); /// 存储开始对象
        }

        // 获取开始对象的pid
        let pid = sobj[pidName];
        // 获取父级对象
        let pobj = map[pid];
        if (!pid || pid == 0 || !pobj) return res;

        // 满足条件
        while (pid && pid != 0 && pobj) {
            // 父级id不为空或者0且父级得存在于map中（因为为空或者0则是顶级）
            res.unshift(pobj); // 插入数组第一位

            if (eid && eid == pid) {
                pid = 0; // 终止循环
            } else {
                pid = pobj[pidName];
                pobj = map[pid];
            }
        }
        console.log('查询层级关联数据对象数组', res);
        return res;
    },

    checkOverdue(string) {
        // x以毫秒为单位
        const selectTime = moment(string).format('x');
        const currentTime = moment().format('x');
        return currentTime > selectTime;
    },

    /**
     * 字符串转二维码
     * @param {*} ref 展示二维码图片的ref
     * @param {*} opts 配置对象
     * @param {*} qrobj 二维码对象
     * @returns 二维码对象
     */
    str2qrcode(ref, opts = {}, qrobj = null) {
        if (qrobj) {
            // 清空二维码
            qrobj.clear();
            // 重新生成
            qrobj.makeCode(opts.text);
            return qrobj;
        } else {
            let options = {
                width: 100,
                height: 100,
                colorDark: '#000000',
                colorLight: '#ffffff',
                correctLevel: QRCode.CorrectLevel.H
            };

            // 传入的配置覆盖默认的配置
            let config = Object.assign(options, opts);
            console.log(ref);
            return new QRCode(ref, config);
        }
    },

    /**
     * 匹配指定对象数组的指定对象的指定属性值
     * @param {Object} arr          对象数组
     * @param {String} key          匹配的值
     * @param {String} labelName    返回的对象指定属性值
     * @param {String} valueName    匹配的对象的属性
     * @returns 对象的指定属性值
     */
    getLVText(arr = [], key, labelName = 'label', valueName = 'value') {
        let o = arr.find(it => it[valueName] == key);
        return o ? o[labelName] : '';
    },



}

function buildTree(arr, parentId, childrenArray) {
    arr.forEach((item) => {
        if (item.parentId === parentId) {
            item.children = [];
            buildTree(arr, item.id, item.children);
            childrenArray.push(item);
        }
    });
}

export function arrayToTree(input, parentId) {
    const array = [];
    buildTree(input, parentId, array);
    return array.length > 0 ? (array.length > 1 ? array : array[0]) : {};
}
